package com.d2.framework.config;

import cn.hutool.json.JSONUtil;
import com.d2.common.domain.Result;
import com.d2.common.util.SpringContextUtils;
import com.d2.modules.system.permission.entity.vo.LoginUser;
import com.d2.framework.util.JwtUtils;
import com.d2.modules.system.permission.entity.User;
import com.d2.modules.system.permission.entity.vo.JWTLoginResult;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collection;

public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    private final AuthenticationManager authenticationManager;

    public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
        super.setFilterProcessesUrl("/login");
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request,
                                                HttpServletResponse response) throws AuthenticationException {

        // 从输入流中获取到登录的信息
        try {
            User user = SpringContextUtils.getBean(ObjectMapper.class).readValue(request.getInputStream(), User.class);
            return authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword())
            );
        } catch (BadCredentialsException e) {
            throw new BadCredentialsException("用户名或密码错误", e);
        } catch (JsonParseException e) {
            e.printStackTrace();

        } catch (IOException e) {
            throw new BadCredentialsException("请求参数不正确", e);
        }
        throw new BadCredentialsException("登录失败");
    }

    // 成功验证后调用的方法
    // 如果验证成功，就生成token并返回
    @Override
    protected void successfulAuthentication(HttpServletRequest request,
                                            HttpServletResponse response,
                                            FilterChain chain,
                                            Authentication authResult) throws IOException, ServletException {

        LoginUser LoginUser = (LoginUser) authResult.getPrincipal();
        System.out.println("LoginUser:" + LoginUser.toString());

        String role = "";
        Collection<? extends GrantedAuthority> authorities = LoginUser.getAuthorities();
        for (GrantedAuthority authority : authorities) {
            role = authority.getAuthority();
        }

        String token = JwtUtils.createToken(LoginUser.getId(),LoginUser.getUsername(), role);
        //String token = JwtTokenUtils.createToken(LoginUser.getUsername(), false);
        // 返回创建成功的token
        // 但是这里创建的token只是单纯的token
        // 按照jwt的规定，最后请求的时候应该是 `Bearer token`
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        String tokenStr = JwtUtils.TOKEN_PREFIX + token;
        JWTLoginResult result = new JWTLoginResult(LoginUser.getId(), LoginUser.getUsername(), tokenStr);
        response.getWriter().write(JSONUtil.toJsonStr(Result.success(result)));
    }

    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException {
        response.setCharacterEncoding("UTF-8");
        Result<Object> result = new Result<>();
        result.setCode(1001);
        result.setMsg(failed.getMessage());
        response.setContentType("application/json");
        response.getWriter().write(JSONUtil.toJsonStr(result));
    }
}
